package org.dromara.common.web.filter;

import org.dromara.common.core.utils.StringUtils;
import org.springframework.http.HttpMethod;

import jakarta.servlet.*;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;


/**
 * @Author 刘武贵
 * @Date 2024/8/15 22:00
 * @Description 功能描述：防止XSS攻击的过滤器
 */
/**
 * XssFilter类实现了Filter接口，用于在Web应用中过滤掉用户输入中的XSS攻击代码
 * XSS（Cross-Site Scripting）跨站脚本攻击，是一种通过注入客户端脚本代码，当用户浏览包含恶意代码的网页时，
 * 利用JavaScript等语言在浏览器客户端执行恶意代码，从而达到窃取信息、破坏网站和向用户网站植入病毒等攻击目的
 * 通过适当的输入过滤和输出编码，可以有效防止XSS攻击
 */
public class XssFilter implements Filter {

    /**
     * 排除链接
     */
    public List<String> excludes = new ArrayList<>();

    /**
     * 初始化过滤器
     * 从web.xml中获取排除的URL列表，并存储下来，以便在过滤器中使用
     *
     * @param filterConfig 过滤器配置对象，可以通过它获取初始化参数
     * @throws ServletException 如果过滤器初始化失败，抛出此异常
     */
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        // 获取初始化参数"excludes"的值，这个参数用于指定需要排除的URL
        String tempExcludes = filterConfig.getInitParameter("excludes");

        // 检查获取到的排除URL是否为空
        if (StringUtils.isNotEmpty(tempExcludes)) {
            // 将字符串形式的URL列表按逗号分隔，转换为字符串数组
            String[] url = tempExcludes.split(StringUtils.SEPARATOR);

            // 遍历URL数组，将每个URL添加到excludes集合中
            for (int i = 0; url != null && i < url.length; i++) {
                excludes.add(url[i]);
            }
        }
    }

    /**
     * 重写doFilter方法，以实现过滤请求的功能
     * 本方法的主要目的是处理XSS攻击（跨站脚本攻击），通过替换请求对象来过滤恶意脚本
     *
     * @param request  请求对象，包含请求的参数等信息
     * @param response 响应对象，用于向客户端发送响应
     * @param chain    过滤器链对象，用于调用下一个过滤器或目标资源
     * @throws IOException           当发生输入输出异常时抛出
     * @throws ServletException      当发生Servlet异常时抛出
     */
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
        throws IOException, ServletException {
        // 将请求和响应对象转换为HTTP请求和响应的类型
        HttpServletRequest req = (HttpServletRequest) request;
        HttpServletResponse resp = (HttpServletResponse) response;

        // 检查当前请求的URL是否需要排除，如果需要排除，则直接调用下一个过滤器或目标资源
        if (handleExcludeURL(req, resp)) {
            chain.doFilter(request, response);
            return;
        }
        // 创建XssHttpServletRequestWrapper对象，用于过滤请求参数中的恶意脚本
        XssHttpServletRequestWrapper xssRequest = new XssHttpServletRequestWrapper((HttpServletRequest) request);
        // 使用过滤后的请求对象调用下一个过滤器或目标资源
        chain.doFilter(xssRequest, response);
    }

    /**
     * 处理排除URL的过滤逻辑
     *
     * 此方法主要用于确定某些特定URL是否应被排除在过滤操作之外，基于请求的方法类型及URL本身
     *
     * @param request  当前的HTTP请求，用于获取请求的URL和方法
     * @param response  当前的HTTP响应，虽然在此方法中未直接使用，但包含以便于上下文切换或扩展功能
     * @return 如果请求的方法为null、GET或DELETE，则返回true，表示这些请求不进行过滤；
     *         否则，检查URL是否匹配预设的排除规则，匹配则返回true，不匹配则返回false
     */
    private boolean handleExcludeURL(HttpServletRequest request, HttpServletResponse response) {
        // 获取当前请求的URL路径
        String url = request.getServletPath();
        // 获取当前请求的方法（如GET、POST等）
        String method = request.getMethod();
        // GET DELETE 不过滤
        if (method == null || HttpMethod.GET.matches(method) || HttpMethod.DELETE.matches(method)) {
            // 如果请求方法为null、GET或DELETE，则直接返回true，表示这些请求不进行过滤
            return true;
        }
        // 判断请求的URL是否匹配预设的排除规则
        // 如果匹配，则返回true，表示该URL不在过滤范围内；如果不匹配，则返回false，表示该URL需要进行过滤
        return StringUtils.matches(url, excludes);
    }

    /**
     * 销毁当前实例的方法
     *
     * 此方法被重写自父类或接口，其主要作用是在不再需要使用当前实例时，
     * 调用它来释放该实例所占用的资源池。在默认情况下，该方法没有具体实现，
     * 但可以在子类中被覆盖以执行特定的销毁逻辑。
     */
    @Override
    public void destroy() {

    }
}
